package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.account.client.UserAccountFeignClient;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.user.client.VipServiceConfigFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    private VipServiceConfigFeignClient vipServiceConfigFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private TrackInfoFeignClient trackInfoFeignClient;

    @Autowired
    private UserAccountFeignClient userAccountFeignClient;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Autowired
    private RabbitService rabbitService;



    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public String submitOrder(OrderInfoVo orderInfoVo, Long userId) {
        //  1.  校验签名！
        Map map = JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        map.put("payWay", "");
        SignHelper.checkSign(map);

        //  2.  校验流水号，防止重复提交订单！
        String tradeNo = orderInfoVo.getTradeNo();
        //  获取到缓存的流水号
        String tradeNoKey = "tradeNo:" + userId + ":info";
        //  缓存的流水号，就是页面的流水号！
        //  定义lua脚本：
        String luaScript = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(luaScript);
        redisScript.setResultType(Long.class);
        //  执行lua脚本；
        Long count = (Long) this.redisTemplate.execute(redisScript, Arrays.asList(tradeNoKey), tradeNo);
        if (count == 0) {
            //  重复提交,抛出异常
            throw new GuiguException(ResultCodeEnum.ORDER_SUBMIT_REPEAT);
        }
        //  声明订单号
        String orderNo = UUID.randomUUID().toString().replaceAll("-", "");
        //  判断支付类型：
        if (!orderInfoVo.getPayWay().equals(SystemConstant.ORDER_PAY_ACCOUNT)) {
            //  在线支付：支付宝或微信 发送延迟消息.
            this.saveOrder(orderNo, orderInfoVo, userId);
        } else {
            //  余额支付
            //  1.  检查与锁定可用余额  service-account 这个微服务！
            AccountDeductVo accountDeductVo = new AccountDeductVo();
            accountDeductVo.setOrderNo(orderNo);
            accountDeductVo.setUserId(userId);
            accountDeductVo.setAmount(orderInfoVo.getOrderAmount());
            accountDeductVo.setContent("扣减与锁定余额:" + orderInfoVo.getOrderDetailVoList().get(0).getItemName());
            //  远程调用这个方法
            Result result = userAccountFeignClient.checkAndDeduct(accountDeductVo);
            if (200 != result.getCode()) {
                //  抛出异常，说明扣减失败！
                throw new GuiguException(result.getCode(), result.getMessage());
            }
            //  2.  保存订单信息
            this.saveOrder(orderNo, orderInfoVo, userId);
            //  3.  保存用户交易信息 tingshu_user user_paid_track user_paid_album user_vip_service
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setUserId(userId);
            userPaidRecordVo.setOrderNo(orderNo);
            userPaidRecordVo.setItemType(orderInfoVo.getItemType());
            //  给购买项目Id赋值；
            List<Long> itemIdList = orderInfoVo.getOrderDetailVoList().stream().map(OrderDetailVo::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);
            //  远程调用：
            Result userResult = userInfoFeignClient.savePaidRecord(userPaidRecordVo);
            if (200 != userResult.getCode()) {
                throw new GuiguException(211, "新增购买记录异常");
            }
        }
        //  返回订单编号
        return orderNo;
    }

    /**
     * 保存订单信息
     *
     * @param orderNo
     * @param orderInfoVo
     * @param userId
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveOrder(String orderNo, OrderInfoVo orderInfoVo, Long userId) {
        //        order_derate
        //        order_detail
        //        order_info
        //  创建订单对象
        OrderInfo orderInfo = new OrderInfo();
        //  赋值
        orderInfo.setUserId(userId);
        orderInfo.setOrderTitle(orderInfoVo.getOrderDetailVoList().get(0).getItemName());
        orderInfo.setOrderNo(orderNo);
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        orderInfo.setOriginalAmount(orderInfoVo.getOriginalAmount());
        orderInfo.setDerateAmount(orderInfoVo.getDerateAmount());
        orderInfo.setOrderAmount(orderInfoVo.getOrderAmount());
        orderInfo.setItemType(orderInfoVo.getItemType());
        orderInfo.setPayWay(orderInfoVo.getPayWay());
        //  保存 当执行完成插入之后，就可以获取到主键自增： @TableId(type = IdType.AUTO)
        orderInfoMapper.insert(orderInfo);
        //  先获取前端传递订单内容： {itemId: 3, itemName: "1年", itemUrl: "http://39.98.123.211/group1/vip.png", itemPrice: "70.00"} order_detail
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (!CollectionUtils.isEmpty(orderDetailVoList)) {
            for (OrderDetailVo orderDetailVo : orderDetailVoList) {
                //  创建订单明细对象
                OrderDetail orderDetail = new OrderDetail();
                //  赋值：
                orderDetail.setOrderId(orderInfo.getId());
                orderDetail.setItemId(orderDetailVo.getItemId());
                orderDetail.setItemName(orderDetailVo.getItemName());
                orderDetail.setItemUrl(orderDetailVo.getItemUrl());
                orderDetail.setItemPrice(orderDetailVo.getItemPrice());
                //  保存：
                orderDetailMapper.insert(orderDetail);
            }
        }
        //  减免：{derateType: "1406", derateAmount: "50.00", remarks: "vip打折"}  order_derate
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (!CollectionUtils.isEmpty(orderDerateVoList)) {
            for (OrderDerateVo orderDerateVo : orderDerateVoList) {
                //  创建减免对象
                OrderDerate orderDerate = new OrderDerate();
                //  赋值
                orderDerate.setOrderId(orderInfo.getId());
                orderDerate.setDerateType(orderDerateVo.getDerateType());
                orderDerate.setDerateAmount(orderDerateVo.getDerateAmount());
                orderDerate.setRemarks(orderDerateVo.getRemarks());
                //  保存：
                orderDerateMapper.insert(orderDerate);
            }
        }
        //  判断支付方式
        if (orderInfoVo.getPayWay().equals(SystemConstant.ORDER_PAY_ACCOUNT)) {
            //  修改订单状态； UNPAID--->PAID;
            this.orderPaySuccess(orderNo);
        } else {
            //  发送延迟消息: 两种：一种死信队列，一种是延迟插件{首选}
            this.rabbitService.sendDealyMessage(MqConst.EXCHANGE_CANCEL_ORDER, MqConst.ROUTING_CANCEL_ORDER, orderNo, MqConst.CANCEL_ORDER_DELAY_TIME);
        }
    }

    /**
     * 修改订单状态
     *
     * @param orderNo
     */
    private void orderPaySuccess(String orderNo) {
        //  order_info.order_status = PAID;
        //  改状态之前，判断是否已经支付;
        OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
        //  判断
        if (!orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_PAID)) {
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            //  修改订单状态
            this.orderInfoMapper.updateById(orderInfo);
        }
    }

    @Override
    public void updateOrderStatus(String orderNo) {
        //  获取订单对象
        OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
        if (orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_PAID)){
            return;
        }
        //  修改订单状态
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
        //  修改订单状态
        this.updateById(orderInfo);

        //  记录购买的内容： savePaidRecord(UserPaidRecordVo userPaidRecordVo);
        UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
        userPaidRecordVo.setUserId(orderInfo.getUserId());
        userPaidRecordVo.setOrderNo(orderNo);
        userPaidRecordVo.setItemType(orderInfo.getItemType());
        //  专辑Id ，声音Id，vipId;
        List<Long> list = orderInfo.getOrderDetailList().stream().map(OrderDetail::getItemId).collect(Collectors.toList());
        userPaidRecordVo.setItemIdList(list);
        //  远程调用
        userInfoFeignClient.savePaidRecord(userPaidRecordVo);
    }

    @Override
    public IPage<OrderInfo> findUserPage(Page<OrderInfo> orderInfoPage, Long userId, String orderStatus) {
        //  调用mapper方法
        IPage<OrderInfo> iPage = orderInfoMapper.selectUserPage(orderInfoPage,userId,orderStatus);
        for (OrderInfo orderInfo : iPage.getRecords()) {
            orderInfo.setPayWayName(orderInfo.getPayWay().equals(SystemConstant.ORDER_PAY_ACCOUNT)?"余额":"在线");
            orderInfo.setOrderStatusName(this.getOrderStatusNameByOrderStatus(orderInfo.getOrderStatus()));
        }
        //  返回对象
        return iPage;
    }

    private String getOrderStatusNameByOrderStatus(String orderStatus) {
        //  获取订单状态名称
        return orderStatus.equals(SystemConstant.ORDER_STATUS_UNPAID)?"未支付":orderStatus.equals(SystemConstant.ORDER_STATUS_PAID)?"已支付":"已取消";
    }

    @Override
    public void cancelOrder(String orderNo) {
        //  直接更新数据
        //  先查询是否有订单对象.
        OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
        //  取消订单时候，属于未付款才能取消！ UNPAID PAID CANCEL
        if (null != orderInfo && orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_UNPAID)) {
            //  未付款的时候，取消订单
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            //  更新订单状态
            this.updateById(orderInfo);
        }
    }

    /**
     * 根据订单编号查询订单信息
     *
     * @param orderNo
     * @return
     */
    public OrderInfo getOrderInfoByOrderNo(String orderNo) {
        //  简单的查询：
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getOrderNo, orderNo);
        OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);
        //  赋值付款方式： 支付方式：1101-微信 1102-支付宝 1103-账户余额
        orderInfo.setPayWayName(orderInfo.getPayWay().equals(SystemConstant.ORDER_PAY_ACCOUNT)?"余额":"在线");
        //  后续还想查询订单明细:
        orderInfo.setOrderDetailList(orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId,orderInfo.getId())));
        return orderInfo;
    }

    @Override
    public OrderInfoVo trade(TradeVo tradeVo, Long userId) {
        // 声明变量
        BigDecimal originalAmount = new BigDecimal("0.00");
        BigDecimal derateAmount = new BigDecimal("0.00");
        BigDecimal orderAmount = new BigDecimal("0.00");

        //  订单明细与减免列表
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
        //  判断用户购买的类型
        if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_ALBUM)) {
            //  购买专辑 购买过的专辑将专辑信息存储到 user_paid_album  tradeVo.getItemId()=专辑Id
            Result<Boolean> isPaidAlbumResult = this.userInfoFeignClient.isPaidAlbum(tradeVo.getItemId());
            Assert.notNull(isPaidAlbumResult, "调用用户服务失败");
            Boolean result = isPaidAlbumResult.getData();
            if (result) {
                //  抛出异常
                throw new GuiguException(ResultCodeEnum.REPEAT_BUY_ERROR);
            }
            //  没有购买专辑; 获取到当前专辑对象
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(tradeVo.getItemId());
            Assert.notNull(albumInfoResult, "调用专辑服务失败");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo, "专辑不存在");

            //  有打折-查询用户的身份
            Result<UserInfoVo> userInfoVoResult = this.userInfoFeignClient.getUserInfoVo(userId);
            Assert.notNull(userInfoVoResult, "调用用户服务失败");
            UserInfoVo userInfoVo = userInfoVoResult.getData();

            //  原始价格
            originalAmount = albumInfo.getPrice();
            //  是属于vip！ 但是，vip 不能过期！
            if (userInfoVo.getIsVip() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
                //  普通用户打折
                if (albumInfo.getVipDiscount().compareTo(new BigDecimal("-1")) != 0) {
                    //  折扣价格 1000 8 实际800； 打折：200； 1000*(10-8)=2000;
                    derateAmount = originalAmount.multiply(new BigDecimal("10").subtract(albumInfo.getVipDiscount())).divide(new BigDecimal("10"), 2, BigDecimal.ROUND_HALF_UP);
                }
            } else {
                //  普通用户打折
                if (albumInfo.getDiscount().compareTo(new BigDecimal("-1")) != 0) {
                    //  折扣价格 1000 8 实际800； 打折：200； 1000*(10-8)=2000;
                    derateAmount = originalAmount.multiply(new BigDecimal("10").subtract(albumInfo.getDiscount())).divide(new BigDecimal("10"), 2, BigDecimal.ROUND_HALF_UP);
                }
            }
            //  实际金额
            orderAmount = originalAmount.subtract(derateAmount);

            //  给订单明细赋值：
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            //  传递是专辑Id
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemPrice(orderAmount);
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemName(albumInfo.getAlbumTitle());
            orderDetailVoList.add(orderDetailVo);

            //  给减免金额赋值！
            if (derateAmount.compareTo(new BigDecimal("0.00")) == 1) {
                //  有折扣；
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setRemarks("专辑打折");
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVoList.add(orderDerateVo);
            }

        } else if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_TRACK)) {
            // 购买声音
            //  判断当前购买的声音数量应该大于等于0;
            if (tradeVo.getTrackCount() < 0) {
                //  说明数据异常
                throw new GuiguException(ResultCodeEnum.ARGUMENT_VALID_ERROR);
            }
            //  tradeVo: {"itemType":"1002","itemId":18031,"trackCount":10}
            //  根据声音Id 获取到声音对象；select * from track_info where id > 18031 limit trackCount;
            Result<List<TrackInfo>> trackInfoListResult = trackInfoFeignClient.findPaidTrackInfoList(tradeVo.getItemId(), tradeVo.getTrackCount());
            List<TrackInfo> trackInfoList = trackInfoListResult.getData();
            //  计算声音价格：原始价格，减免价格--{不包含折扣}，实际价格
            //  单价 = 专辑单价
            Result<AlbumInfo> albumInfoResult = this.albumInfoFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId());
            AlbumInfo albumInfo = albumInfoResult.getData();
            //  originalAmount = 单价*数据;
            originalAmount = tradeVo.getTrackCount() == 0 ? albumInfo.getPrice() : albumInfo.getPrice().multiply(new BigDecimal(tradeVo.getTrackCount()));
            orderAmount = originalAmount;
            //  赋值订单明细:
            for (TrackInfo trackInfo : trackInfoList) {
                //  创建对象
                OrderDetailVo orderDetailVo = new OrderDetailVo();
                orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                orderDetailVo.setItemName(trackInfo.getTrackTitle());
                orderDetailVo.setItemId(trackInfo.getId());
                //  获取到当前声音的单价
                orderDetailVo.setItemPrice(albumInfo.getPrice());
                orderDetailVoList.add(orderDetailVo);
            }

        } else {
            //  购买vip {"itemType":"1003","itemId":2}
            //  根据itemId查询vip_service_config表记录;
            Result<VipServiceConfig> vipServiceConfigResult = this.vipServiceConfigFeignClient.getVipServiceConfig(tradeVo.getItemId());
            Assert.notNull(vipServiceConfigResult, "调用vip服务失败");
            VipServiceConfig vipServiceConfig = vipServiceConfigResult.getData();
            Assert.notNull(vipServiceConfig, "vip服务不存在");
            //  原始价格
            originalAmount = vipServiceConfig.getPrice();
            //  实际价格
            orderAmount = vipServiceConfig.getDiscountPrice();
            //  折扣价格
            derateAmount = originalAmount.subtract(orderAmount);

            //  购买vip的明细列表：
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemPrice(orderAmount);
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVo.setItemName(vipServiceConfig.getName());
            orderDetailVoList.add(orderDetailVo);

            //  给减免金额赋值！
            if (derateAmount.compareTo(new BigDecimal("0.00")) == 1) {
                //  有折扣；
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setRemarks("vip打折");
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVoList.add(orderDerateVo);
            }
        }
        //  给对象赋值数据：
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        //  给个随机字符串;
        String tradeNo = UUID.randomUUID().toString().replaceAll("-", "");
        //  设置缓存的tradeNoKey=tradeNo:userId:info; value=tradeNo
        String tradeNoKey = "tradeNo:" + userId + ":info";
        //  有两种方式： 一种 set 一种setnx 当key 不存在的时候生效！
        this.redisTemplate.opsForValue().set(tradeNoKey, tradeNo, 10, TimeUnit.MINUTES);
        //  给订单编号赋值：
        orderInfoVo.setTradeNo(tradeNo);
        //  支付方式给默认空
        orderInfoVo.setPayWay("");
        orderInfoVo.setItemType(tradeVo.getItemType());
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        //  赋值时间戳：
        orderInfoVo.setTimestamp(SignHelper.getTimestamp());
        //  赋值签名: 防止用户通过某种方式篡改提交的数据； 制作这个签名:
        Map map = JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        //  除去sign字段,然后使用TreeMap进行排序，将value用|进行拼接！再拼接上一个盐{一个固定字符串} 最后将这个字符串进行MD5加密并返回！
        String sign = SignHelper.getSign(map);
        orderInfoVo.setSign(sign);
        //  返回对象
        return orderInfoVo;
    }
}
